/*
 * TMap Designer - A Thematic Map design software
 * Copyright (C) 2006 World Health Organization
 * http://www.who.int
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
package who.tmap.ui.cd;

import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import javax.swing.DefaultCellEditor;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;

import org.apache.log4j.Logger;
import org.bushe.swing.event.EventBus;

import who.tmap.bo.AttrDef;
import who.tmap.bo.ThematicMap;
import who.tmap.bo.cd.CDColorPattern;
import who.tmap.bo.cd.CDTypes;
import who.tmap.bo.cd.ColorPatternClassification;
import who.tmap.bo.cd.ColorPatternRange;
import who.tmap.bo.cd.impl.CartographicDisplayFactory;
import who.tmap.common.TMapException;
import who.tmap.services.ColorService;
import who.tmap.toolkit.impl.AnnotationsHelper;
import who.tmap.ui.event.ColorPatternNbRangesHasChanged;
import who.tmap.ui.util.JComboBoxColor;

public class JPanelCDColorPatternHelper {
	
    private static Logger logger = Logger.getLogger(JPanelCDColorPatternHelper.class);

	private JPanelCDColorPattern jPanel;
	
	public JPanelCDColorPatternHelper( JPanelCDColorPattern jPanel ) {
		this.jPanel =jPanel;
	}
	
	/**
	 * Retrieves unique captions from the captions list, using a case insensitive comparison
	 * The result is stored in a collection and its size is used as the number of ranges
	 * @throws TMapException
	 */
	@SuppressWarnings("unchecked")
	public void processNonNumeric() throws TMapException {
		
		ThematicMap thematicMap =jPanel.getThematicMap();
		
		List<List> rows =thematicMap.getData().getRows();
		// structure that gives indexes for each caption
		jPanel.rangeForValues =new LinkedList<String>();
		
		// Look for attributes column indexes
		int dataColIdx =-1;
		List<AttrDef> headers =( List<AttrDef> ) thematicMap.getData().getHeader();
		
		MainLoop:
		for( int ct =0; ct < headers.size(); ct++ ) {
			AttrDef attrDef =( AttrDef )headers.get( ct );
			
			if ( ( ( String )jPanel.getJComboBoxDataColumn().getSelectedItem() ).equalsIgnoreCase( attrDef.getName() ) ) {
				dataColIdx =ct;
				break MainLoop;
			}
		}
		
		for( List row : rows ) {
			Object obj =row.get(dataColIdx);
			if( obj != null ) {
				String val =obj.toString();
				String[] rangeForValuesAsArray =new String[ jPanel.rangeForValues.size() ];
				rangeForValuesAsArray =jPanel.rangeForValues.toArray( rangeForValuesAsArray );				
				Arrays.sort( rangeForValuesAsArray,String.CASE_INSENSITIVE_ORDER );
				Integer indexOfDuplicatedValue =Arrays.binarySearch( rangeForValuesAsArray, val, String.CASE_INSENSITIVE_ORDER );
				
				if( indexOfDuplicatedValue < 0 ) {
					jPanel.rangeForValues.add( val.toLowerCase() );
				}
			}
		}
		
		Collections.sort( jPanel.rangeForValues, String.CASE_INSENSITIVE_ORDER );
		
		populateRanges( jPanel.rangeForValues.size() );
		
		jPanel.getJTextFieldNbRanges().setText( String.valueOf( jPanel.rangeForValues.size() ) );
	}
	
    public void populateRanges( int nbRanges ) {
    	
    	logger.debug( "populateRanges nbRanges=[" + nbRanges + "]" );
    	
    	CDColorPattern cdc =null;
    	
		ThematicMap thematicMap =jPanel.getThematicMap();
    	
    	if ( thematicMap != null 
    			&& thematicMap.getCartographicDisplay() instanceof CDColorPattern ) {
    		cdc =(CDColorPattern)thematicMap.getCartographicDisplay();
    	}
    	
    	if ( cdc == null ) {
    		try {
				cdc =(CDColorPattern)CartographicDisplayFactory.getInstance().get( CDTypes.COLOR_PATTERN );
				if ( thematicMap != null ) {
					thematicMap.setCartographicDisplay( cdc );
				}
			} catch (TMapException e) {
				logger.error( e );
			}
    	}
    	
    	Map<Integer,ColorPatternRange> ranges =cdc.getRanges();
    	
    	Map<Integer,ColorPatternRange> newRanges =new TreeMap<Integer,ColorPatternRange>();
 
    	for( int ct =1; ct <= nbRanges; ct++ ) {
    		ColorPatternRange range =ranges != null
    								? (ColorPatternRange)ranges.get( new Integer( ct ) )
    								: null;
    		if ( range == null ) {
    			try {
					range =(ColorPatternRange)CartographicDisplayFactory.getInstance().get( CDTypes.COLOR_PATTERN_RANGE );
					range.setRangeIdx( ct );
					
					if ( jPanel.getJComboBoxAttrClassification().getSelectedIndex() == ColorPatternClassification.NON_NUMERIC ) {
						range.setFrom( ct );
						range.setTo( ct + .1 );
			    		String caption =ct <= jPanel.rangeForValues.size() ? jPanel.rangeForValues.get( ct - 1 ) : ""; 
			    		range.setCaption( caption );
					} else {
						range.setFrom( 0 );
						range.setTo( 0 );
					}
					
					range.setColor( ColorPatternRange.DEFAULT_RANGE_COLORS[ (ct -1) % ColorPatternRange.DEFAULT_RANGE_COLORS.length ] );
					range.setFillPattern( 0 );
					
				} catch (TMapException e) {
					logger.error( e );
				}
				
    		} else if ( jPanel.getJComboBoxAttrClassification().getSelectedIndex() == ColorPatternClassification.NON_NUMERIC ) {
				range.setFrom( ct );
				range.setTo( ct + .1 );
	    		String caption =jPanel.rangeForValues.get( ct - 1 ); 
	    		range.setCaption( caption );
	    		//range.setColor( ColorPatternRange.DEFAULT_RANGE_COLORS[ jPanel.rangeForValues.indexOf(caption)] );
    		}
    		
			newRanges.put( new Integer( ct ), range );
    	}
    	
    	new AnnotationsHelper().applyRanges( nbRanges, thematicMap.getAnnotations(), jPanel.getJComboBoxAttrClassification().getSelectedIndex() );
    	
       	cdc.setRanges( newRanges );    	
       	applyColorColumnEditors( newRanges );
    	jPanel.dataModel.setRanges( newRanges );
    }
    
	public void refreshData() {
		int nbRanges =0;
		CDColorPattern cd =null;
		
		ThematicMap thematicMap =jPanel.getThematicMap();
		
		if ( thematicMap.getCartographicDisplay() instanceof CDColorPattern ) {
			cd =(CDColorPattern)thematicMap.getCartographicDisplay();
			
			if ( cd != null ) {
				applyColorColumnEditors( cd.getRanges() );
				
				jPanel.dataModel.setRanges( cd.getRanges() );
				
				jPanel.getJComboBoxJoinIdColumn().setSelectedItem( cd.getJoinIdColumnName() );
				jPanel.getJComboBoxDataColumn().setSelectedItem( cd.getDataColumnName() );
	
				jPanel.getJTextFieldMissingDataRange().setText( Integer.toString( cd.getMissingDataRangeIdx() ) );
				 
				if ( cd.getRanges() != null ) {
					nbRanges =cd.getRanges().size();
				} else {
					nbRanges =0;
				}
				
				jPanel.getJTextFieldNbRanges().setText( String.valueOf( nbRanges ) );
				jPanel.getJComboBoxAttrClassification().setSelectedIndex( cd.getClassification() );
			} 
		}
		
		if ( nbRanges == 0 ) {
			// Special case for non-numeric as nbRanges is computed using the data set
			if ( jPanel.getJComboBoxAttrClassification().getSelectedIndex() == ColorPatternClassification.NON_NUMERIC ) {
				try {
					processNonNumeric();
					
					nbRanges =jPanel.rangeForValues.size();
				} catch (TMapException e) {
					logger.error( e );
					
					nbRanges =JPanelCDColorPattern.DEFAULT_NB_RANGES;
					populateRanges( nbRanges );
				}
			} else {
				nbRanges =JPanelCDColorPattern.DEFAULT_NB_RANGES;
				populateRanges( nbRanges );
			}
			//populateRanges( nbRanges );
		}
		
		jPanel.getJTextFieldNbRanges().setText( Integer.toString( nbRanges ) );		
	
	}
	
	private void applyColorColumnEditors( Map<Integer, ColorPatternRange> ranges ) {
		if ( ranges != null ) {
			jPanel.colorEditors =new TableCellEditor[ ranges.size() ];
			jPanel.colorRenderers =new TableCellRenderer[ ranges.size() ];	
			ColorService[] colorServices =new ColorService[ ranges.size() ];
			
			for( int ct =0; ct < ranges.size(); ct++ ) {
				JComboBoxColor cb =new JComboBoxColor();

				jPanel.colorEditors[ ct ] =new DefaultCellEditor( cb );	
				jPanel.colorRenderers[ ct ] =new JColorTableComboBoxRenderer( cb.getColorService() );
				colorServices[ ct ] =cb.getColorService();
				cb.enableItemStateChanged( false );
				cb.setSelectedColor( ranges.get( ct + 1 ).getColor() );
				cb.enableItemStateChanged( true );				
			}
			
			jPanel.dataModel.setColorServices( colorServices );
		}
	}
	
	public void updateRanges() {
		String nbRangesStr =jPanel.getJTextFieldNbRanges().getText();
		
		try {
			int nbRanges =Integer.parseInt( nbRangesStr );
			
			populateRanges( nbRanges );
			
			//jPanel.notifyObservers( JFrameCartographicDisplay.NB_RANGES_UPDATED_MSG + String.valueOf( nbRanges ) + "_" + jPanel.getJComboBoxAttrClassification().getSelectedIndex() );
			EventBus.publish( new ColorPatternNbRangesHasChanged( nbRanges, 
															   jPanel.getJComboBoxAttrClassification().getSelectedIndex() ) );
			
			jPanel.refreshRanges();
						
			jPanel.setRangesUpdated( false );
		} catch( Exception e2 ) {
			logger.debug( e2 );
			jPanel.getJTextFieldNbRanges().setText( String.valueOf( JPanelCDColorPattern.DEFAULT_NB_RANGES ) );
		}
		
		//logger.debug( "updateRanges" );

	}
	
}
